package com.yc.tomcat1;


import com.yc.tomcat2.javax.servlet.YcWebServlet;
import com.yc.tomcat2.javax.servlet.YcServletContext;
import org.apache.log4j.Logger;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import java.io.File;
import java.io.FileFilter;
import java.io.FileInputStream;
import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.Enumeration;

/**
 * @author zwei
 * @create 2024-07-25 20:39
 */
public class TomcatServer {
    private static Logger logger = Logger.getLogger(TomcatServer.class);

    public static void main(String[] args) {
        logger.info("tomcat server start");
        TomcatServer tomcatServer = new TomcatServer();
        int port = tomcatServer.parsePortFromXml();
        logger.debug("服务器端口为：" + port);
        tomcatServer.startServer(port);
    }

    public void startServer(int port) {
        boolean flag = true;
        String packageName = "com.yc";
        String packagePath = packageName.replaceAll("\\.", "/");
        //服务器启动的时候 扫描它所有的类classes 查找@YcWebServlet的class，存到map中
        try {
            Enumeration<URL> files = Thread.currentThread().getContextClassLoader().getResources(packagePath);
            while (files.hasMoreElements()) {
                URL url = files.nextElement();
                logger.info("正在扫描的包路径为：" + url.getFile());
                findPackageClasses(url.getFile(), packageName);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        try {
            ServerSocket ss = new ServerSocket(port);
            logger.debug("服务器启动了，配置的端口为：" + port);
            //TODO:可以读取server.xml中关于是否开启线程的配置项--决定是否使用线程池
            while (flag) {
                Socket s = ss.accept();
                logger.debug("客户端：" + s.getRemoteSocketAddress() + "连接上了服务器");
                TaskService taskService = new TaskService(s);
                Thread thread = new Thread(taskService);
                thread.start();
            }
        } catch (Exception e) {
            e.printStackTrace();
            logger.error("服务器启动失败");
        }

    }

    /**
     * @param packagePath com/yc
     * @param packageName com.yc
     */
    private void findPackageClasses(String packagePath, String packageName) {
        if (packagePath.startsWith("/")) {
            packagePath = packagePath.substring(1);
        }
        //取这个路径下所有的字节码文件
        File file = new File(packagePath);
        File[] classFiles = file.listFiles(new FileFilter() {
            @Override
            public boolean accept(File pathname) {
                if (pathname.getName().endsWith(".class") || pathname.isDirectory()) {
                    return true;
                } else {
                    return false;
                }
            }
        });
        //System.out.println(classFiles);
        if (classFiles != null && classFiles.length > 0) {
            for (File cf : classFiles) {
                if (cf.isDirectory()) {
                    findPackageClasses(cf.getAbsolutePath(), packageName + "." + cf.getName());
                } else {
                    //是字节码文件，则利用类加载器加载这个class文件
                    URLClassLoader uc = new URLClassLoader(new URL[]{});
                    try {
                        Class cls = uc.loadClass(packageName + "." + cf.getName().replaceAll(".class", ""));
                        //logger.info("加载了一个类："+cls.getName());
                        if (cls.isAnnotationPresent((YcWebServlet.class))) {
                            logger.info("加载了一个类：" + cls.getName());
                            YcWebServlet anno = (YcWebServlet) cls.getAnnotation(YcWebServlet.class);
                            String url = anno.value();
                            //通过注解的value()方法取出url地址 存到YcServletContext.servletClass这个map中
                            YcServletContext.servletClass.put(url,cls);
                        }

                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }

    private int parsePortFromXml() {
        int port = 8080;
        String path = System.getProperty("user.dir") + File.separator + "conf" + File.separator + "server.xml";
        logger.info(path);
        try (InputStream iis = new FileInputStream(path)) {
            DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
            DocumentBuilder documentBuilder = factory.newDocumentBuilder();
            Document document = documentBuilder.parse(iis);
            NodeList nodeList = document.getElementsByTagName("Connector");
            for (int i = 0; i < nodeList.getLength(); i++) {
                Element node = (Element) nodeList.item(i);
                port = Integer.parseInt(node.getAttribute("port"));
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return port;
    }
}
